package com.shadowtorcher.stdisk.serverlet;

import com.shadowtorcher.soulknife.SoulKnifeDB;
import com.shadowtorcher.soulknife.exception.BeanConfigErrorException;
import com.shadowtorcher.stdisk.bean.*;
import com.shadowtorcher.stdisk.logic.*;
import com.shadowtorcher.stdisk.util.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.sql.SQLException;
import java.util.*;

@RestController
@RequestMapping(value = "/api")
public class ApiController {
    private static final String USER = "com.shadowtorcher.stdisk.user";
    // 上传文件存储目录
    private static final String UPLOAD_DIRECTORY = "upload";


    @RequestMapping(path = "checkidentity", method = RequestMethod.POST)
    public void checkIdentity(HttpServletRequest request, HttpServletResponse response) {
        HttpResponser checkIdentityResponse = new HttpResponser();
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        password = StaticUtils.getMd5(password);
        try {
            List<UserBean> list = SoulKnifeDB.selectWhere("UserName = ? and Password = ?", username, password).setClass(UserBean.class);

            if (list.isEmpty()) {
                checkIdentityResponse.setCode(1);  //用户名或密码输入不正确

            } else {
                checkIdentityResponse.setCode(0);        //用户名且密码输入正确
                request.getSession().setAttribute(USER, list.get(0));     //把USER放到状态0的request中，确认登录状态
            }
        } catch (Exception e) {
            checkIdentityResponse.setCode(-1);   //出现未知错误
            checkIdentityResponse.setMessage(e.toString());
        }
        checkIdentityResponse.response(response);
    }

    @RequestMapping(path = "checkmd5andhash", method = RequestMethod.GET)
    public void checkMd5AndHash(HttpServletRequest request, HttpServletResponse response) {
        String md5value = request.getParameter("md5value").toUpperCase();
        String hashvalue = request.getParameter("hashvalue").toUpperCase();
        long filesize = Long.parseLong(request.getParameter("filesize"));
        UserBean user = StaticUtils.getUser(request.getSession());
        HttpResponser responder = new HttpResponser();
        try {
            Permission permission = new Permission(user.getPermissionID());
            if (user.getUsedSpace() + filesize > Long.parseLong(permission.get("size"))) {
                responder.setCode(3);
                responder.setMessage("no speace");
                responder.response(response);
                return;
            }
            List<PhysicsFileBean> list = SoulKnifeDB.selectWhere("Md5 = ? or Hash = ?", md5value, hashvalue).setClass(PhysicsFileBean.class);
            if (list.isEmpty()) {
                responder.setCode(0);
            } else {
                String physicsFileID = list.get(0).getPhysicsFileID();
                responder.setCode(1);
                responder.setMessage(physicsFileID);
            }
        } catch (Exception e) {
            e.printStackTrace();
            responder.setCode(-1);
            responder.setMessage(e.toString());
        }
        responder.response(response);
    }

    @RequestMapping(path = "getcomment", method = RequestMethod.GET)
    public void getGetComment(HttpServletRequest request, HttpServletResponse response) {
        GetCommentResponser getCommentResponser = new GetCommentResponser();
        String sharedFileID = request.getParameter("sharedFileID");
        try {
            List<CommentBean> list = SoulKnifeDB.selectWhere("SharedFileID = ?", sharedFileID).setClass(CommentBean.class);
            getCommentResponser.setComment(list);
            getCommentResponser.setCode(0);
        } catch (Exception e) {
            getCommentResponser.setCode(-1);
            getCommentResponser.setMessage(e.toString());
        }
        getCommentResponser.response(response);
    }

    @RequestMapping(path = "getcomment", method = RequestMethod.POST)
    public void postGetComment(HttpServletRequest request, HttpServletResponse response) {
        String comment = request.getParameter("comment");
        String sharedFileID = request.getParameter("sharedFileID");
        GetCommentResponser getCommentResponser = new GetCommentResponser();
        try {
            CommentBean commentBean = new CommentBean();
            commentBean.setSharedFileID(sharedFileID);
            commentBean.setComment(comment);
            commentBean.save();
            getCommentResponser.setCode(0);
        } catch (Exception e) {
            getCommentResponser.setCode(-1);
            getCommentResponser.setMessage(e.toString());
        }
        getCommentResponser.response(response);
    }

    @RequestMapping(path = "getpublicfile", method = RequestMethod.GET)
    public void getPublicFile(HttpServletRequest request, HttpServletResponse response) {
        GetPublicFileResponser getPublicFileResponser = new GetPublicFileResponser();
        String uuid = request.getParameter("uuid");
        UserBean user = StaticUtils.getUser(request.getSession());
        try {
            if (user == null) {
                List<UserBean> list = SoulKnifeDB.selectWhere("UserID = ?", uuid).setClass(UserBean.class);
                if (list.size() == 0) {
                    getPublicFileResponser.setCode(-1);
                    getPublicFileResponser.setMessage("no such user");
                    getPublicFileResponser.response(response);
                    return;
                }
                user = list.get(0);
            }
            List<LogicFileBean> list = SoulKnifeDB.selectWhere("Public != 0 and UserID = ?", user.getUserID()).setClass(LogicFileBean.class);
            getPublicFileResponser.setCode(0);
            getPublicFileResponser.setMessage("success");
            getPublicFileResponser.setItems(list);
        } catch (Exception e) {
            getPublicFileResponser.setCode(-1);
            getPublicFileResponser.setMessage(e.toString());
        }
        getPublicFileResponser.response(response);
    }

    @RequestMapping(path = "getspace", method = RequestMethod.GET)
    public void getSpace(HttpServletRequest request, HttpServletResponse response) {
        UserBean user = StaticUtils.getUser(request.getSession());
        GetSpaceResponser getSpaceResponser = new GetSpaceResponser();
        try {
            SpaceGetter spaceGeter = new SpaceGetter(user);
            getSpaceResponser.setName(user.getUserName());
            getSpaceResponser.setUsed(spaceGeter.getUsed());
            getSpaceResponser.setTotal(spaceGeter.getTotal());
            getSpaceResponser.setUuid(user.getUserID());
        } catch (SQLException e) {
            e.printStackTrace();
            getSpaceResponser.setCode(-1);
            getSpaceResponser.setMessage(e.toString());
        }
        getSpaceResponser.response(response);
    }

    @RequestMapping(path = "logoff", method = RequestMethod.POST)
    public void logOff(HttpServletRequest request, HttpServletResponse response) {
        HttpResponser LogOffServlet = new HttpResponser();
        try {
            request.getSession().invalidate();
            LogOffServlet.setCode(0);                     //成功登出,前台跳转页面
        } catch (Exception e) {
            LogOffServlet.setCode(-1);                  //未知的错误
        }
        LogOffServlet.response(response);
    }

    @RequestMapping(path = "makepublic", method = RequestMethod.POST)
    public void makePublic(HttpServletRequest request, HttpServletResponse response) {
        String uuid = request.getParameter("uuid");
        HttpResponser httpResponser = new HttpResponser();
        try {
            LogicFileBean logicFile = SoulKnifeDB.selectWhere("LogicFileID = ?", uuid).setClass(LogicFileBean.class).get(0);
            logicFile.setPublic(logicFile.getPublic() == 0 ? 1 : 0);
            SoulKnifeDB.updateWhere(logicFile, "LogicFileID = ?", logicFile.getLogicFileID()).setClass(LogicFileBean.class);
            httpResponser.setCode(0);
            httpResponser.setMessage("success");
        } catch (Exception e) {
            httpResponser.setCode(-1);
            httpResponser.setMessage(e.toString());
        }
        httpResponser.response(response);
    }

    @RequestMapping(path = "register", method = RequestMethod.POST)
    public void register(HttpServletRequest request, HttpServletResponse response) {
        HttpResponser registerHttpResponser = new HttpResponser();
        String username = request.getParameter("username");
        try {
            List<UserBean> list = SoulKnifeDB.selectWhere("Username =?", username).setClass(UserBean.class);
            if (list.isEmpty()) {
                // 注册储存
                String password = request.getParameter("password");
                String permission = request.getParameter("permission");
                if (password == null || permission == null) {
                    //数据库中不存在同名用户，则可以注册,返回0
                    registerHttpResponser.setCode(0);
                }
                //有密码和权限则是注册
                else {
                    Register register = new Register();
                    register.insertUser(username, password, permission);
                }
            }
            //数据库存在同名用户，不能注册，返回1
            else {
                registerHttpResponser.setCode(1);
            }
        } catch (Exception e) {
            registerHttpResponser.setCode(-1);            //未知的错误
            registerHttpResponser.setMessage(e.toString());
        }
        registerHttpResponser.response(response);
    }

    @RequestMapping(path = "uploadfilebyshare", method = RequestMethod.POST)
    public void uploadFileByShare(HttpServletRequest request, HttpServletResponse response) {
        HttpResponser responder = new HttpResponser();
        String uuid = request.getParameter("uuid");
        String current_path = request.getParameter("current_path");
        String fileName = request.getParameter("fileName");
        UserBean userBean = StaticUtils.getUser(request.getSession());
        try {
            PhysicsFileBean physicsFileBean = SoulKnifeDB.selectWhere("PhysicsFileID = ?", uuid).setClass(PhysicsFileBean.class).get(0);
            StaticUtils.addReferenceCount(physicsFileBean.getPhysicsFileID());
            LogicFileBean logicFileBean = new LogicFileBean();
            logicFileBean.setCreationDate(new Date());
            logicFileBean.setModifiDate(new Date());
            logicFileBean.setPublic(0);
            logicFileBean.setUserID(userBean.getUserID());
            logicFileBean.setPhysicsFileID(physicsFileBean.getPhysicsFileID());
            logicFileBean.setLogicPath(current_path);
            logicFileBean.setLogicFileName(fileName);
            logicFileBean.save();
            userBean.setUsedSpace(userBean.getUsedSpace() + physicsFileBean.getFileSize());
            SoulKnifeDB.updateWhere(userBean, "UserID = ?", userBean.getUserID()).setClass(UserBean.class);
            responder.setCode(1);
        } catch (Exception e) {
            e.printStackTrace();
            responder.setCode(-1);
            responder.setMessage(e.toString());
        }
        responder.response(response);
    }

    @RequestMapping(path = "uploadfile", method = RequestMethod.POST)
    public void uploadFile(@RequestParam("current_path") String current_path, @RequestParam("uploadFile") MultipartFile file,
                           HttpServletRequest request, HttpServletResponse response) throws IOException, BeanConfigErrorException, SQLException {
        UserBean userBean = StaticUtils.getUser(request.getSession());

        String uploadPath = request.getServletContext().getRealPath("./") + File.separator + UPLOAD_DIRECTORY;
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }
        if (current_path == null && file == null) {
            response.sendRedirect(request.getContextPath() + "/html/main.html#uploaderror");
            return;
        }

        String file_name = file.getOriginalFilename();
        String stringUUID = StaticUtils.generatorStringUUID();

        // 获取上传文件
        String filePath = uploadPath + File.separator + stringUUID;
        File local_file = new File(filePath);
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(local_file));
        out.write(file.getBytes());
        out.flush();
        out.close();

        FileUtil fileUtil = new FileUtil(local_file);
        PhysicsFileBean physicsFileBean = new PhysicsFileBean();
        physicsFileBean.setPhysicsFileID(stringUUID);
        physicsFileBean.setFileSize(fileUtil.getFileSize());
        physicsFileBean.setHash(fileUtil.getHash());
        physicsFileBean.setMd5(fileUtil.getMd5());
        physicsFileBean.setReferenceCount(1);
        physicsFileBean.save();

        LogicFileBean logicFileBean = new LogicFileBean();
        logicFileBean.setCreationDate(new Date());
        logicFileBean.setModifiDate(new Date());
        logicFileBean.setPublic(0);
        logicFileBean.setUserID(userBean.getUserID());
        logicFileBean.setPhysicsFileID(stringUUID);
        logicFileBean.setLogicPath(current_path);
        logicFileBean.setLogicFileName(file_name);
        logicFileBean.save();

        userBean.setUsedSpace(userBean.getUsedSpace() + physicsFileBean.getFileSize());
        SoulKnifeDB.updateWhere(userBean, "UserID = ?", userBean.getUserID()).setClass(UserBean.class);

        FileSender fileSender = new FileSender(physicsFileBean, local_file);
        fileSender.send();
        response.sendRedirect(request.getContextPath() + "/html/main.html");
    }

    @RequestMapping(path = "deletefile", method = RequestMethod.POST)
    public void deleteFile(HttpServletRequest request, HttpServletResponse response) {
        String uuid = request.getParameter("uuid");
        HttpResponser httpResponser = new HttpResponser();
        try {
            FileDeleter fileDeleter = FileDeleter.getByUuid(uuid, StaticUtils.getUser(request.getSession()));
            if (fileDeleter == null) {
                httpResponser.setCode(-1);
                httpResponser.setMessage("no file");
            } else {
                switch (fileDeleter.doDelete()) {
                    case FileDeleter.SUB_REFERENCE:
                    case FileDeleter.REALLY_DELETE:
                        httpResponser.setCode(1);
                        httpResponser.setMessage("success");
                        break;
                    case FileDeleter.MOVE_TO_BIN:
                        httpResponser.setCode(0);
                        httpResponser.setMessage("move to bin");
                        break;
                    case FileDeleter.ERROR:
                    default:
                        httpResponser.setCode(-1);
                        httpResponser.setMessage("error");
                }
            }
        } catch (SQLException | NullPointerException e) {
            e.printStackTrace();
            httpResponser.setCode(-1);
            httpResponser.setMessage(e.toString());
        }
        httpResponser.response(response);
    }

    @RequestMapping(path = "getdownloadurl", method = RequestMethod.GET)
    public void getDownloadURL(HttpServletRequest request, HttpServletResponse response) {
        UserBean user = StaticUtils.getUser(request.getSession());
        String uuid = request.getParameter("uuid");
        DownloadUrlResponser downloadUrlResponser = new DownloadUrlResponser();
        try {
            DownloadUrlGenerator downloadUrlGenerator = new DownloadUrlGenerator(user);
            downloadUrlResponser.setUrls(downloadUrlGenerator.getUrl(uuid));
            downloadUrlResponser.setCode(0);
            downloadUrlResponser.setMessage("success");
        } catch (Exception e) {
            e.printStackTrace();
            downloadUrlResponser.setCode(-1);
            downloadUrlResponser.setMessage(e.toString());
        }
        downloadUrlResponser.response(response);
    }

    @RequestMapping(path = "geturl", method = RequestMethod.GET)
    public void getURL(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String uuid = request.getParameter("uuid");
        String localurl = "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/s/";
        FileShare fileshare = new FileShare();
        fileshare.share(uuid);//存表
        SharedFileBean sharedfilebean = new SharedFileBean();
        try {
            List<SharedFileBean> list = SoulKnifeDB.selectWhere("LogicFileID = ?", uuid).setClass(SharedFileBean.class);
            sharedfilebean = list.get(0);
        } catch (BeanConfigErrorException | SQLException e) {
            e.printStackTrace();
        }
        response.getWriter().print(localurl + sharedfilebean.getUrl());
    }

    @RequestMapping(path = "listfile", method = RequestMethod.GET)
    public void listFile(HttpServletRequest request, HttpServletResponse response) {
        UserBean user = StaticUtils.getUser(request.getSession());
        ListFileResponder responder = new ListFileResponder();
        try {

            String startString = request.getParameter("start");
            String lengthString = request.getParameter("length");
            String path = URLDecoder.decode(request.getParameter("path"), "utf-8");
            if (startString == null && lengthString == null) {
                responder.setCode(0);
                responder.setMessage("success");
                responder.setItems(new FileLister(user).listAllFile(path));
            } else if (startString == null || lengthString == null) {
                responder.setCode(-1);
                responder.setMessage("start and length should be provided at same time");
            } else {
                int start = Integer.parseInt(startString);
                int length = Integer.parseInt(lengthString);
                List<LogicFileBean> list = new FileLister(user).listAllFile(path, start, length);
                if (list.size() < length) {
                    responder.setCode(1);
                } else {
                    responder.setCode(0);
                }
                responder.setMessage("success");
                responder.setItems(list);
            }
        } catch (Exception e) {
            e.printStackTrace();
            responder.setCode(-1);
            responder.setMessage(e.toString());
        }
        responder.response(response);
    }

    @RequestMapping(path = "movefile", method = RequestMethod.POST)
    public void moveFile(HttpServletRequest request, HttpServletResponse response) {
        String uuid = request.getParameter("uuid");
        HttpResponser httpResponser = new HttpResponser();
        try {
            String newPath = request.getParameter("newpath");
            String newFileName = request.getParameter("newfilename");
            FileMover fileMover = FileMover.getFromUUID(uuid);
            if (fileMover == null) {
                httpResponser.setCode(-1);
                httpResponser.setMessage("no such file");
            } else if (newPath != null && newFileName != null) {
                fileMover.move(newPath, newFileName);
                httpResponser.setCode(0);
                httpResponser.setMessage("success");
            } else if (newPath != null) {
                fileMover.move(newPath);
                httpResponser.setCode(0);
                httpResponser.setMessage("success");
            } else if (newFileName != null) {
                fileMover.rename(newFileName);
                httpResponser.setCode(0);
                httpResponser.setMessage("success");
            } else {
                httpResponser.setCode(0);
                httpResponser.setMessage("success");
            }
        } catch (Exception e) {
            httpResponser.setCode(-1);
            httpResponser.setMessage(e.toString());
        }
        httpResponser.response(response);
    }

    @RequestMapping(path = "searchfile", method = RequestMethod.GET)
    public void searchFile(HttpServletRequest request, HttpServletResponse response) {
        UserBean user = StaticUtils.getUser(request.getSession());
        SearchFileResponder responder = new SearchFileResponder();
        String type = request.getParameter("type");
        String key = request.getParameter("key");
        String order = request.getParameter("order");
        try {
            String startString = request.getParameter("start");
            String lengthString = request.getParameter("length");
            if (order != null) {
                responder.setCode(0);
                responder.setMessage("success");
                responder.setItems(new FileLister(user).listRecentFile());
            } else if (startString == null && lengthString == null) {
                responder.setCode(0);
                responder.setMessage("success");
                responder.setItems(new FileLister(user).searchFile(key, type));
            } else if (startString == null || lengthString == null) {
                responder.setCode(-1);
                responder.setMessage("start and length should be provided at same time");
            } else {
                int start = Integer.parseInt(startString);
                int length = Integer.parseInt(lengthString);
                List<LogicFileBean> list = new FileLister(user).searchFile(key, type, start, length);
                if (list.size() < length) {
                    responder.setCode(1);
                } else {
                    responder.setCode(0);
                }
                responder.setMessage("success");
                responder.setItems(list);
            }
        } catch (Exception e) {
            e.printStackTrace();
            responder.setCode(-1);
            responder.setMessage(e.toString());
        }
        responder.response(response);
    }

    public static class ListFileResponder extends HttpResponser {
        ListFileResponder() {
            super();
        }

        void setItems(List<LogicFileBean> list) throws SQLException {
            LinkedList<TreeMap<String, Object>> tmp = new LinkedList<TreeMap<String, Object>>();
            for (LogicFileBean logicFile : list) {
                TreeMap<String, Object> item = new TreeMap<>();
                item.put("LogicPath", logicFile.getLogicPath());
                if (logicFile.getLogicFileID() == null) {
                    item.put("FileType", "dir");
                } else {
                    item.put("FileType", FileTypePatternGenerator.getType(logicFile.getLogicFileName()));
                    item.put("LogicFileID", logicFile.getLogicFileID());
                    item.put("LogicFileName", logicFile.getLogicFileName());
                    item.put("Public", logicFile.getPublic());
                    item.put("CreationDate", logicFile.getCreationDate());
                    item.put("ModifiDate", logicFile.getModifiDate());
                    item.put("FileSize", FileLister.getFileSize(logicFile));
                }
                tmp.add(item);
            }
            map.put("items", tmp);
        }

        @Override
        public void response(HttpServletResponse response) {
            map.computeIfAbsent("items", k -> new ArrayList<>());
            super.response(response);
        }
    }

    private class GetPublicFileResponser extends HttpResponser {
        GetPublicFileResponser() {
            super();
        }

        void setItems(List<LogicFileBean> list) throws SQLException {
            LinkedList<TreeMap<String, Object>> tmp = new LinkedList<TreeMap<String, Object>>();
            for (LogicFileBean logicFile : list) {
                TreeMap<String, Object> item = new TreeMap<>();
                item.put("LogicPath", logicFile.getLogicPath());
                if (logicFile.getLogicFileID() == null) {
                    item.put("FileType", "dir");
                } else {
                    item.put("FileType", FileTypePatternGenerator.getType(logicFile.getLogicFileName()));
                    item.put("LogicFileID", logicFile.getLogicFileID());
                    item.put("LogicFileName", logicFile.getLogicFileName());
                    item.put("Public", logicFile.getPublic());
                    item.put("CreationDate", logicFile.getCreationDate());
                    item.put("ModifiDate", logicFile.getModifiDate());
                    item.put("FileSize", FileLister.getFileSize(logicFile));
                }
                tmp.add(item);
            }
            map.put("items", tmp);
        }

        @Override
        public void response(HttpServletResponse response) {
            map.computeIfAbsent("items", k -> new ArrayList<>());
            super.response(response);
        }
    }

    private class GetCommentResponser extends HttpResponser {
        public void setComment(List<CommentBean> comment) {
            put("Comment", comment);
        }
    }

    private class GetSpaceResponser extends HttpResponser {
        void setUsed(long used) {
            put("used", used);
        }

        void setTotal(long total) {
            put("total", total);
        }

        void setName(String name) {
            put("name", name);
        }

        void setUuid(String uuid) {
            put("uuid", uuid);
        }
    }

    private class DownloadUrlResponser extends HttpResponser {
        void setUrls(List<String> urls) {
            super.put("urls", urls);
        }

        @Override
        public void response(HttpServletResponse response) {
            map.computeIfAbsent("urls", k -> new ArrayList<>());
            super.response(response);
        }
    }

    private class SearchFileResponder extends ListFileResponder {
        SearchFileResponder() {
            super();
        }
    }
}
